查看原文
其他

分享一个基本不可能被检测到的hook方案

梦野间 看雪学苑 2022-07-01


看雪论坛作者ID:梦野间



1


背景


最近项目里需要通过hook对APP的行为进行监控,需要满足如下几个需求:

(1)兼容性好,能够兼容99.9%的APP。

(2)不能被APP的安全机制检测到导致APP逻辑不正常或者hook失败。

(3)能够实时地与PC端进行通信,上报监控信息。

(4)hook时机要非常早,不能出现漏检的情况。

(5)因为APP量非常大,需要一种通用化方案,不能出现对特定APP进行定制的情况。

因此,考虑了下述几种方案:


Frida Server hook方案


这也是之前在使用的方案。基于frida的hook,是一种常见的hook方案,该方案实现简单,可扩展性强,易于调试和修改,hook代码可以随时修改,还能通过RPC方便地在PC上进行交互,能够绕过大部分hook检测,自然也就成了逆向人员最常见的一种分析手段。

缺点:

该方案虽然有很多优势,但也存在着一些缺陷:

(1)该方案过于出名,导致很多加固厂商对其进行了针对性的检测,在部分保护强度较高的APP上无法正常运行。

(2)frida本身虽然兼容性虽然较好,但在注入过程中,仍会出现在部分APP上无法正常运行的情况。

因此,该方案虽然在90%的情况下能够满足需求,但是需求1和需求2仍有小概率不满足。


Frida Gadget hook方案


这是Frida Server在部分场景下被检测到,而且在部分APP中无法正常注入,从而萌生的思路。

为了实现持久化,frida给出了Gadget方案,该方案通过对APK进行重打包,向lib目录中添加Gadget的动态链接库,然后反编译dex,在dex中添加加载该动态链接库的代码,以此实现持久化。

缺点:

(1)重打包方案是Gadget的最原始使用方式。该方案虽然避免了运行时的注入过程,提高了兼容性,但是对APK的重打包,需要绕过APK本身的签名校验和文件校验,虽然可以用自动化脚本实现签名校验和文件校验的绕过,但是如果被检测的APP是使用汇编编写的open()函数,也是绕不过去的。需求2不满足。

(2)除了重打包方案,因为之前自己做过SO的静态注入,所以也考虑过对APP安装后解压出来的so文件进行静态注入,这样就可以避过签名校验和文件校验。但是这种方案比较依赖于so文件的加载时机,虽然加固后的APP,壳so往往都会在最早的时候进行加载,但是也难以保证适用于所有APP。需求4不满足。


XPOSED方案


XPOSED方案比较容易被检测,而且在与PC的交互上也不是太好。因此需求3和需求2都不满足。


定制ROM方案


定制ROM方案类似于脱壳机,基本上不可能被检测,而且兼容性也非常好。但是该方案基本上只能用于开放源代码的系统机型,而且系统的编译时间非常长,每一点微小的改动都需要重新编译并刷机并重启系统,开发调试费时费力,后续更新极其不方便。而且与PC端的通信也比较麻烦。

该方案属于一种可行方案,但是耗时过长,成本太高,属于最后实在没有其他办法了之后的办法。



2


基于系统组件替换的定制ROM方案


总结上述几种方案,可以发现,目前的几种方案主要有以下几个问题需要解决:

(1)对APP或进程的侵入式修改,会在侵入过程中留下痕迹,这些痕迹容易被hook检测机制检测到。

(2)与PC端进行实时通信。

(3)降低开发与维护成本。


系统so替换


这是早期的思路,通过对系统so进行静态注入,使得APP在启动时加载系统so,顺带的把我们自定义的so加载起来,因为这个自定义的so名称可以自定义,以及Android的碎片化,APP基本上不可能根据so的名称来对这种hook方式进行检测。

然而,想法很丰满,现实很骨感,实际测试下来发现,Android系统启动时,会先生成Zygote进程,在Zygote进程中加载所有必需的so,后续所有的APP进程都是Zygote fork出来的,因此,替换后的系统so的加载时机并不是在APP启动时。

那么,能不能启动一个线程,不断循环检测要hook的APP是否被fork了呢?然而事实又给了我打击,Zygote在fork进程之前,会先等待进程中的所有子线程结束,以免某些子线程中的任务还没完成就进行了fork,导致功能异常。因此,如果启动一个线程,就会导致整个系统直接就起不来了(因为还有一些包括Launcher在内的系统APP需要通过Zygote fork出来)。

有没有一个so,是在APP启动时进行加载的呢?通过对比Zygote进程和一个简单demo的maps列表,发现/system/lib/hw/gralloc.default.so这个so是在APP启动时加载的,经过一番百度,这个so应该是与APP图形界面渲染有关的so,而根据so的命名可以看出,这个so是有可能根据硬件的不同而变化的。而且经过测试,这个so是在Activity被启动时才加载的,因此,假如APP在Application中进行了一些操作,这种方案是监控不到的。


Riru插件定制


其实Riru的整体思路和系统so替换的思路是很相似的,不过个人感觉系统so替换的思路要比Riru的思路实现起来更简单一些。

Riru相比于系统so替换的强大之处在于他对APP启动时会调用的函数进行了hook,并以插件的形式对外提供了接口,免去了自己再去研究源码,寻找hook点。

接下来编写了一个插件使用Frida Gadget进行尝试,然后再次发现悲催了,Frida Gadget的Listen模式中,需要PC端找到APP进程然后恢复程序运行,但是Riru提供的接口中,进程的相关环境还没有完全初始化好,使用PS命令查找不到进程。

最后的最后,在Frida的JS API中找到了网络相关的API,因此采用了Frida Gadget的script的模式,然后在hook接口中实现了一个TCP服务器,将hook信息通过网络通信及adb转发发送到PC端。

整个流程为:

(1)编写Riru插件gadget_loader:功能为读取配置文件(放置在/data/local/tmp目录下),根据配置文件,若当前APP是需要检测的APP,则加载libgadget.so。

(2)在插件中添加libgadget.so及其配置文件。

(3)安装gadget_loader插件。

(4)将libloader.so的配置文件和libgadget.so需要执行的脚本放置到/data/local/tmp目录下。

(5)libgadget.so脚本通过hook对应API监控APP接口调用行为,并通过网络连接将监控信息发送至PC。



3


总结


最后总结一下这个方案的几个特点:

(1)不需要对APK进行修改,也不需要对进程进行注入,因此可以避开APP的检测机制。

(2)方案为了方便,最后采用了Riru插件进行实现,这使得APP有可能检测到存在Riru插件,如果碰到这种情况,完全可以采用系统so替换的方案,然后模仿Riru的思路hook几个API,实现Gadget so的加载,这样就基本上不可能被检测到了。

(3)因为整个方案只是替换一个系统so,并添加了几个文件到系统中,实现了APK的零侵入,对系统的侵入也非常的小,兼容性会非常好(主要取决于Frida Gadget中是否有bug)。

(4)与PC端的交互采用的是socket通信,这个通信的接口是可自定义的,在保证了与PC端交互的同时又能避免固定端口被检测。

(5)整个方案的实现过程非常简单,低版本的系统上可以直接替换系统so,高版本的系统因为一些Android的安全机制,只能采用Magisk插件的形式进行so的替换,不过高版本系统上的root本来就几乎都是使用Magisk,这点小小的前置要求就也就几近于无了。

保密原因,代码就不放了。




 


看雪ID:梦野间

https://bbs.pediy.com/user-home-706972.htm

*本文由看雪论坛 梦野间 原创,转载请注明来自看雪社区





# 往期推荐

1.由2021ByteCTF引出的intent重定向浅析

2.全网最详细CVE-2014-0502 Adobe Flash Player双重释放漏洞分析

3.基于linker实现so加壳补充-------从dex中加载so

4.利用__libc_csu_init控制64位寄存器

5.一个堆题inndy_notepad的练习笔记

6.源码编译——Xposed源码编译详解



公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存